/**
 * 
 */
package com.hp.bon.sgw.wh.channel;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import ocs.soap.AccountQueryRequest;
import ocs.soap.AccountQueryResponse;
import ocs.soap.OcsSoapBindingStub;
import ocs.soap.RechargeRequest;
import ocs.soap.RechargeResponse;
import ocs.soap.RefundRequest;
import ocs.soap.RefundResponse;
import ocs.soap.TeleSaleAuthRequest;
import ocs.soap.TeleSaleAuthResponse;
import ocs.soap.pay.AuthenticationRequest;
import ocs.soap.pay.AuthenticationResponse;
import ocs.soap.pay.RechargePayRequest;
import ocs.soap.pay.RechargePayResponse;
import ocs.soap.tele.TeleSaleRebackRequest;
import ocs.soap.tele.TeleSaleRebackResponse;
import ocs.soap.tele.TeleSaleRechargeRequest;
import ocs.soap.tele.TeleSaleRechargeResponse;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.Constants.NodeStatus;
import com.hp.bon.sgw.bean.ProtocalConvertResult;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.convert.DataExchangerImpl;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.InprogressMessageQueue;
import com.hp.bon.sgw.core.channel.IChannel;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.Comutils;
import com.hp.bon.sgw.util.LogTool;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.bon.sgw.wh.utils.Myutils;
import com.hp.bon.sgw.wh.utils.SelfServiceHelper;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.Message;

/**
 * @author sunhaiy
 * 
 */
public class SelfServiceChannel implements IChannel {

	public static final Logger LOG = LoggerFactory.getLogger(SelfServiceChannel.class);
	private SGWChannel channel;
	protected ConfigService confSrv;

	private MultiThreadedHttpConnectionManager connectionManager;
	private HttpClient httpClient;

	public SelfServiceChannel(SGWChannel channel) {
		this.channel = channel;
		confSrv = SpringContext.getInstance().getConfigService();
	}

	@Override
	public SGWChannel getSGWChannel() {
		// TODO Auto-generated method stub
		return this.channel;
	}

	@Override
	public int getThreadPoolSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getThreadPoolQueueSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getThreadPoolActiveCount() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getNodeSendQueueSize(String nodeHostId) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void start() throws IOException {
		LOG.info("start SelfServiceChannel");

		connectionManager = new MultiThreadedHttpConnectionManager();
		httpClient = new HttpClient(connectionManager);

		this.channel.setStatus(NodeStatus.ON_LINE);
		confSrv.updateChannelStatus2DB(this.channel.getId(), NodeStatus.ON_LINE);
	}

	@Override
	public void stop() {
		LOG.info("stop SelfServiceChannel");
		if (connectionManager != null) {
			connectionManager.shutdown();
		}

		this.channel.setStatus(NodeStatus.OFF_LINE);
		confSrv.updateChannelStatus2DB(this.channel.getId(),
				NodeStatus.OFF_LINE);
	}

	private static String CAP_VCRecharge_Balance = "Recharge.UserHome";
	private static String CAP_Query_Balance = "Query.UserHome";
	private static String CAP_VCRefund_Balance = "Refund.UserHome";
	private static String CAP_VCAuth_Balance = "Auth.UserHome";
	private static String HLXK_HostId = "HLXK@027.ctnbc-bon.net";
	private static String Best_Pay_HostId = "WingPay@027.ctnbc-bon.net";
	private static String Tele_Sale_HostId = "TeleSale@027.ctnbc-bon.net";

	@Override
	public int sendRequest(CallParam callParam) {
		Message ccr = (Message) callParam.getRequest();
		AVP destHostAVP = ccr.find(Constants.DIAMETER_AVP_DEST_HOST);
		if (destHostAVP == null) {
			LOG.warn(callParam.getSessionId() + " missing Dest-Host AVP");
			callParam.setRetCode(11104);
			callParam.setResultMsg("missing Dest-Host AVP");

			InprogressMessageQueue.getInstance().removeProgress(
					callParam.getMessageUniqueID());
			callParam.getAsynHandler().onCallResult(callParam);
			return -1;
		}
		String destHost = AVP_UTF8String.queryValue(destHostAVP);
		Node node = ConfigService.name2NodeMap.get(destHost);
		callParam.setToNode(node);
		AVP sgwRootAVP = DccAvpHelper
				.createSGWRootAVPFromMessage((Message) callParam.getRequest());
		ProtocalConvertResult<Element> result = DataExchangerImpl.getInstance()
				.convertFromCCRRequest(callParam.getServiceContextId(),
						sgwRootAVP);
		Element requestElement = null;
		if (result.getRetCode() == Constants.ResultCode.SUCCESS) {
			requestElement = result.getResult();
		} else {
			LOG.warn(callParam.getSessionId() + " Convert CCR to XML failed.");
			callParam.setRetCode(result.getRetCode());
			callParam.setResultMsg(result.getErrMsg());

			InprogressMessageQueue.getInstance().removeProgress(
					callParam.getMessageUniqueID());
			callParam.getAsynHandler().onCallResult(callParam);
			return -1;
		}

		if (HLXK_HostId.equals(destHost)) {
			this.sgwCall(callParam, requestElement.element("Service_Information"));
		} else {
			try {
				String serviceContextId = callParam.getServiceContextId();
				if (serviceContextId.contains(CAP_VCRecharge_Balance)) {
					recharge(callParam, requestElement);
				} else if (serviceContextId.contains(CAP_Query_Balance)) {
					accountQuery(callParam, requestElement);
				} else if (serviceContextId.contains(CAP_VCRefund_Balance)) {
					refund(callParam, requestElement);
				} else if (callParam.getServiceContextId().startsWith(
						CAP_VCAuth_Balance)) {
					authForVC(callParam, requestElement);
				} else {
					callParam.setRetCode(11006);// TODO
					callParam.setResultMsg(serviceContextId
							+ " is not excepted.");
					LOG.warn(serviceContextId + " is not excepted.");
				}
			} catch (Exception e) {
				LOG.warn("call local bon service err:", e);
				int retCode = LogTool.getErrorCodeForBonCallException(e);
				callParam.setRetCode(retCode);
				callParam.setResultMsg("Call ws service err:"
						+ e.getLocalizedMessage());
			}
		}
		InprogressMessageQueue.getInstance().removeProgress(
				callParam.getMessageUniqueID());
		callParam.getAsynHandler().onCallResult(callParam);
		return 0;
	}

	private void sgwCall(CallParam callParam, Element requestXml) {
		StringBuilder reqXML = new StringBuilder();
		reqXML.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><ns3:SgwQueryReq xmlns:ns3=\"http://www.chinatelecom.cn/SGW/SOAP\">");
		reqXML.append("<MID>").append(callParam.getSessionId())
				.append("</MID>");
		reqXML.append("<UID>").append(callParam.getUserName()).append("</UID>");
		reqXML.append("<PWD>").append(callParam.getToNode().getPassword())
				.append("</PWD>");
		reqXML.append("<SCID>").append(callParam.getServiceContextId())
				.append("</SCID>");
		reqXML.append("<DATA><![CDATA[").append(requestXml.asXML())
				.append("]]></DATA>");
		reqXML.append("</ns3:SgwQueryReq></SOAP-ENV:Body></SOAP-ENV:Envelope>");

		LOG.info("----sgwCall----" + reqXML.toString());
		PostMethod post = new PostMethod(callParam.getToNode().getServerUrl());
		try {
			RequestEntity requestEntity = new StringRequestEntity(
					reqXML.toString(), "text/xml", "UTF-8");
			post.setRequestEntity(requestEntity);

			this.genSendRequest(callParam, requestXml);

			httpClient.executeMethod(post);
			String response = post.getResponseBodyAsString();
			LOG.info("----receive response ----" + response);
			XmlMessage sgwAck = parseWSResponse(response);

			int callRc = Integer.parseInt(sgwAck.getRC());
			callParam.setRetCode(callRc);
			callParam.setResultMsg(sgwAck.getDS());
			callParam.setRecvResponse(sgwAck);
		} catch (Exception e) {
			LOG.warn("call ws service err:", e);
			int retCode = LogTool.getErrorCodeForBonCallException(e);
			callParam.setRetCode(retCode);
			callParam.setResultMsg("Call ws service err:"
					+ e.getLocalizedMessage());
		} finally {
			if (post != null) {
				post.releaseConnection();
			}
		}
	}

	static XMLInputFactory inputFactory = XMLInputFactory.newInstance();

	public static XmlMessage parseWSResponse(String response) throws Exception {
		InputStream input = new ByteArrayInputStream(response.getBytes("UTF-8"));
		XMLStreamReader xmlStreamReader = inputFactory
				.createXMLStreamReader(input);
		XmlMessage ack = new XmlMessage(false);
		int nextType = 0;

		boolean seenSgwQueryAck = false;
		while (xmlStreamReader.hasNext()) {
			nextType = xmlStreamReader.next();
			if (nextType == XMLStreamReader.START_ELEMENT) {
				String elName = xmlStreamReader.getLocalName();
				if (elName.equals("SgwQueryAck")) {
					seenSgwQueryAck = true;
				} else if (seenSgwQueryAck) {
					String value = xmlStreamReader.getElementText();
					if (elName.equals("MID")) {
						ack.setMID(value);
					} else if (elName.equals("RTT")) {
						ack.setRTT(value);
					} else if (elName.equals("RC")) {
						ack.setRC(value);
					} else if (elName.equals("SIG")) {
						ack.setSIG(value);
					} else if (elName.equals("DATA")) {
						ack.setDATA(value);
					} else if (elName.equals("DS")) {
						ack.setDS(value);
					}
				}
			}
		}
		try {
			if (ack.getDATA() != null && !"".equals(ack.getDATA().toString())) {
				Document doc = Comutils.parseText(ack.getDATA());
				ack.setDataElement(doc.getRootElement());
			}
		} catch (Exception e) {
			LOG.warn("convert ack DATA to Element failed." + e.toString());
		}
		return ack;
	}

	private void recharge(CallParam callParam, Element requestXml)
			throws DocumentException, RemoteException {
		RechargeRequest rr = SelfServiceHelper
				.convertXMLToRechargeRequest(requestXml);
		RechargeResponse response = null;
		String system_time = new SimpleDateFormat("yyyyMMddHHmmss")
				.format(Calendar.getInstance().getTime());
		if (Tele_Sale_HostId.equals(callParam.getToNode().getHostId())) {

			ocs.soap.tele.OcsSoapBindingStub stub = Myutils
					.getTeleSaleSoapBindingStub(callParam.getToNode()
							.getServerUrl());

			TeleSaleRechargeRequest req = new TeleSaleRechargeRequest();
			req.setRequestId(rr.getRequestId());
			// req.setRequestTime(rr.getRequestTime());
			req.setRequestTime(system_time);
			req.setFc_code(rr.getDestinationId());
			req.setAmount(rr.getRechargeAmount());

			this.genSendRequest(callParam, requestXml);
			TeleSaleRechargeResponse resp = stub.teleSaleRecharge(req);

			if (resp != null) {
				response = new RechargeResponse();
				response.setResponseId(resp.getResponseId());
				response.setResponseTime(resp.getResponseTime());
				response.setResult(resp.getResult());
				response.setBalance(resp.getBalance());
				response.setBalanceUnit(0);
				response.setEffectiveTime("0");
				response.setExpirationTime("0");
			}
		} else if (Best_Pay_HostId.equals(callParam.getToNode().getHostId())) {

			ocs.soap.pay.OcsSoapBindingStub stub = Myutils
					.getPaySoapBindingStub(callParam.getToNode().getServerUrl());

			// RechargePayRequest
			RechargePayRequest req = new RechargePayRequest();
			req.setRequestId(rr.getRequestId());
			// req.setRequestTime(rr.getRequestTime());
			req.setRequestTime(system_time);
			req.setDestinationId(rr.getDestinationId());
			req.setDestinationAttr(rr.getDestinationAttr());
			req.setDestinationAttrDetail(rr.getDestinationAttrDetail());
			req.setBalanceType(rr.getBalanceType());
			req.setBonus(rr.getBonus());
			req.setCardId(rr.getVoucherId());
			req.setObjType(rr.getObjType());
			req.setProlongDays(rr.getProlongDays());
			req.setRechargeAmount(rr.getRechargeAmount());
			req.setRechargeUnit(rr.getRechargeUnit());
			req.setRequestAmount(rr.getRequestAmount());
			LOG.info("----Best_Pay---- " + req.toString());

			this.genSendRequest(callParam, requestXml);
			RechargePayResponse resp = stub.rechargePay(req);
			if (resp != null) {
				// responseBeatPay = new ocs.soap.pay.RechargeResponse();
				response = new RechargeResponse();
				response.setResponseId(resp.getResponseId());
				response.setResponseTime(resp.getResponseTime());
				response.setResult(resp.getResult());
				response.setBalance(resp.getBalance());
				response.setBalanceType(resp.getBalanceType());
				response.setBalanceUnit(resp.getBalanceUnit());
				response.setEffectiveTime(resp.getEffectiveTime());
				response.setExpirationTime(resp.getExpirationTime());
			}
		} else {// RechargeRequest

			LOG.info("HLXK---URL:" + callParam.getToNode().getServerUrl());
			rr.setRequestTime(system_time);
			if (rr.getAreaCode() == null) {
				rr.setAreaCode("");
			}
			LOG.info("HLXK---req:" + rr.toString());
			OcsSoapBindingStub stub = Myutils.getForVCSoapBindingStub(callParam
					.getToNode().getServerUrl());

			this.genSendRequest(callParam, requestXml);
			response = stub.recharge(rr);
		}
		if (response != null) {
			Element e = SelfServiceHelper
					.convertRechargeResponseToXML(response);

			genResponse(callParam, e);

		} else {
			LOG.warn("receive " + callParam.getSessionId()
					+ " response is null");
		}
	}

	private void accountQuery(CallParam callParam, Element requestXml)
			throws RemoteException {
		OcsSoapBindingStub stub = Myutils.getForVCSoapBindingStub(callParam
				.getToNode().getServerUrl());
		AccountQueryRequest rr = SelfServiceHelper
				.convertXMLToAccountQueryRequest(requestXml);

		this.genSendRequest(callParam, requestXml);
		AccountQueryResponse response = stub.accountQuery(rr);
		if (response != null) {
			Element e = SelfServiceHelper
					.convertAccountQueryResponseToXML(response);
			genResponse(callParam, e);
		} else {
			LOG.warn("receive " + callParam.getSessionId()
					+ " response is null");
		}
	}

	private void refund(CallParam callParam, Element requestXml)
			throws RemoteException {
		RefundRequest rr = SelfServiceHelper
				.convertXMLToRefundRequest(requestXml);
		String system_time = new SimpleDateFormat("yyyyMMddHHmmss")
				.format(Calendar.getInstance().getTime());
		RefundResponse response = null;
		if (Tele_Sale_HostId.equals(callParam.getToNode().getHostId())) {

			ocs.soap.tele.OcsSoapBindingStub stub = Myutils
					.getTeleSaleSoapBindingStub(callParam.getToNode()
							.getServerUrl());

			TeleSaleRebackRequest req = new TeleSaleRebackRequest();
			req.setRequestId(rr.getRequestId());
			// req.setRequestTime(rr.getRequestTime());
			req.setRequestTime(system_time);
			req.setOldResponseId(rr.getOldRequestId());
			req.setFcCode(rr.getDestinationId());

			this.genSendRequest(callParam, requestXml);
			TeleSaleRebackResponse ts_response = stub.teleSaleReback(req);
			if (null != ts_response) {
				response = new RefundResponse();
				response.setResponseId(ts_response.getResponseId());
				response.setResponseTime(ts_response.getResponseTime());
				response.setResult(ts_response.getResult());
			}
		} else {
			OcsSoapBindingStub stub = Myutils.getForVCSoapBindingStub(callParam
					.getToNode().getServerUrl());
			this.genSendRequest(callParam, requestXml);
			response = stub.refund(rr);
		}
		if (response != null) {
			Element e = SelfServiceHelper.convertRefundResponseToXML(response);
			this.genResponse(callParam, e);
		} else {
			LOG.warn("receive " + callParam.getSessionId()
					+ " response is null");
		}
	}

	private void authForVC(CallParam callParam, Element requestXml)
			throws RemoteException {

		String system_time = new SimpleDateFormat("yyyyMMddHHmmss")
				.format(Calendar.getInstance().getTime());
		TeleSaleAuthRequest ar = SelfServiceHelper
				.convertXMLToTeleSaleAuthRequest(requestXml);
		TeleSaleAuthResponse response = null;
		if (Best_Pay_HostId.equals(callParam.getToNode().getHostId())) {

			ocs.soap.pay.OcsSoapBindingStub stub = Myutils
					.getPaySoapBindingStub(callParam.getToNode().getServerUrl());

			AuthenticationRequest req = new AuthenticationRequest();
			req.setRequestId(ar.getRequestId());
			// req.setRequestTime(ar.getRequestTime());
			req.setRequestTime(system_time);
			req.setDestinationId(ar.getObjId());
			this.genSendRequest(callParam, requestXml);
			AuthenticationResponse resp = stub.authentication(req);
			if (null != resp) {
				response = new TeleSaleAuthResponse();
				response.setResponseId(resp.getResponseId());
				response.setResponseTime(resp.getResponseTime());
				response.setResult(resp.getResult());
			}
		} else {
			OcsSoapBindingStub stub = Myutils.getForVCSoapBindingStub(callParam
					.getToNode().getServerUrl());

			this.genSendRequest(callParam, requestXml);
			response = stub.teleSaleAuth(ar);
		}
		if (null != response) {
			Element e = SelfServiceHelper
					.convertUserAuthResponseToXML(response);
			this.genResponse(callParam, e);
		} else {
			LOG.warn("receive " + callParam.getSessionId()
					+ " response is null");
		}
	}

	private void genSendRequest(CallParam callParam, Element e) {
		XmlMessage ack = new XmlMessage(true);
		ack.setMsgTime(System.currentTimeMillis());
		ack.setMID(callParam.getSessionId());
		ack.setDataElement(e);
		ack.setLength(e.asXML().length());
		callParam.setSendRequest(ack);
	}

	private void genResponse(CallParam callParam, Element e) {
		XmlMessage ack = new XmlMessage(false);
		ack.setMsgTime(System.currentTimeMillis());
		ack.setMID(callParam.getSessionId());
		ack.setSIG(null);
		ack.setRC(Constants.ResultCode.SUCCESS + "");
		ack.setDataElement(e);
		ack.setLength(e.asXML().length());
		callParam.setRecvResponse(ack);
	}

	@Override
	public void addPersist(Node arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void deletePersist(Node arg0) {
		// TODO Auto-generated method stub

	}

}
